﻿using System;
using System.Threading;

namespace RockBus.Threading
{
    public static class ThreadPoolHelper
    {
        public static ThreadPoolSettingsExt ApplyThreadPoolConfiguration(bool throwOnConfigurationNotFound = false)
        {
            if ((null == ThreadPoolConfigurationSection.Instance) || (null == ThreadPoolConfigurationSection.Instance.ThreadPoolConfiguration))
            {
                if (throwOnConfigurationNotFound)
                {
                    throw new InvalidOperationException("ThreadPoolConfiguration not found.");
                }

                return GetThreadPoolSettings();
            }

            ThreadPoolConfiguration tpc = null;
            try
            {
                tpc = ThreadPoolConfigurationSection.Instance.ThreadPoolConfiguration;
            }
            catch (Exception exc)
            {
                tpc = null;
                if (throwOnConfigurationNotFound)
                {
                    throw new InvalidOperationException("ThreadPoolConfiguration not found.", exc);
                }
            }

            if (null != tpc)
            {
                ThreadPoolSettings tps = new ThreadPoolSettings(ThreadPoolConfigurationSection.Instance.ThreadPoolConfiguration);
                SetThreadPoolSettings(tps);
            }
            return GetThreadPoolSettings();
        }

        public static ThreadPoolSettingsExt GetThreadPoolSettings()
        {
            int wt, cpt;
            ThreadPool.GetAvailableThreads(out wt, out cpt);

            int minwt, minct;
            ThreadPool.GetMinThreads(out minwt, out minct);

            int maxwt, maxct;
            ThreadPool.GetMaxThreads(out maxwt, out maxct);

            return new ThreadPoolSettingsExt(minwt, maxwt, minct, maxct, wt, cpt);
        }

        public static void SetThreadPoolSettings(ThreadPoolSettings tps)
        {
            if (!ThreadPool.SetMaxThreads(tps.MaxWorkerThreads, tps.MaxIoThreads))
            {
                throw new InvalidOperationException(string.Format("Unable to set MaxThreads on ThreadPool to {0}/{1}", tps.MaxWorkerThreads, tps.MaxIoThreads));
            }

            if (!ThreadPool.SetMinThreads(tps.MinWorkerThreads, tps.MinIoThreads))
            {
                throw new InvalidOperationException(string.Format("Unable to set MinThreads on ThreadPool to {0}/{1}", tps.MaxWorkerThreads, tps.MaxIoThreads));
            }
        }
    }

    public class ThreadPoolSettings
    {
        public ThreadPoolSettings(int minWorkerThreads, int maxWorkerThreads, int minIoThreads, int maxIoThreads)
        {
            this.MinWorkerThreads = minWorkerThreads;
            this.MaxWorkerThreads = maxWorkerThreads;
            this.MinIoThreads = minIoThreads;
            this.MaxIoThreads = maxIoThreads;
        }

        public ThreadPoolSettings(ThreadPoolConfiguration tpc)
        {
            this.MinWorkerThreads = tpc.MinWorkerThreads;
            this.MaxWorkerThreads = tpc.MaxWorkerThreads;
            this.MinIoThreads = tpc.MinIoThreads;
            this.MaxIoThreads = tpc.MaxIoThreads;
        }

        public int MinWorkerThreads;
        public int MaxWorkerThreads;
        public int MinIoThreads;
        public int MaxIoThreads;

        public override string ToString()
        {
            return string.Format("MinWorkerThreads: {0}; MaxWorkerThreads: {1}; MinIoThreads: {2}; MaxIoThreads: {3}",
                base.ToString(), this.MinWorkerThreads, this.MaxWorkerThreads, this.MinIoThreads, this.MaxIoThreads);
        }
    }

    public class ThreadPoolSettingsExt : ThreadPoolSettings
    {
        public ThreadPoolSettingsExt(int minWorkerThreads, int maxWorkerThreads, int minIoThreads, int maxIoThreads, int availableWorkerThreads, int availableIoThreads)
            : base(minWorkerThreads, maxWorkerThreads, minIoThreads, maxIoThreads)
        {
            this.AvailableWorkerThreads = availableWorkerThreads;
            this.AvailableIoThreads = availableIoThreads;
        }

        public int AvailableWorkerThreads;
        public int AvailableIoThreads;

        public override string ToString()
        {
            return string.Format("{0}; AvailableWorkerThreads: {1}; AvailableIoThreads: {2}",
                base.ToString(), this.AvailableWorkerThreads, this.AvailableIoThreads);
        }
    }
}